package com.qf.service.impl;

import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.qf.dto.UserDto;
import com.qf.enums.CheckType;
import com.qf.enums.LoginResultEnum;
import com.qf.events.MyLoginEvent;
import com.qf.exceptions.AddDataErrorException;
import com.qf.exceptions.MyErrorCode;
import com.qf.exceptions.QueryDataErrorException;
import com.qf.mapper.SysUserInfoMapper;
import com.qf.pojo.SysUserInfo;
import com.qf.service.SysUserService;
import com.qf.utils.MD5Utils;
import com.qf.utils.MyStringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;

import java.util.Date;
import java.util.List;

/**
 * created by 赵文韬 on 2022/3/14 16:50
 */
@Service
@Transactional
public class SysUserInfoImpl implements SysUserService {
    private ApplicationContext applicationContext;
    private SysUserInfoMapper sysUserInfoMapper;
    @Autowired
    public void setApplicationContext(ApplicationContext applicationContext) {
        this.applicationContext = applicationContext;
    }

    @Autowired
    public void setSysUserInfoMapper(SysUserInfoMapper sysUserInfoMapper) {
        this.sysUserInfoMapper = sysUserInfoMapper;
    }

    @Override
    public SysUserInfo findById(Long userId) {
        return sysUserInfoMapper.findById(userId);
    }

    @Override
    public PageInfo<SysUserInfo> findAll(int pageSize, int pageNum) {
        PageHelper.startPage(pageSize,pageNum);/*????*/
        List<SysUserInfo> all = sysUserInfoMapper.findALL();
        PageInfo<SysUserInfo> sysUserInfoPageInfo = new PageInfo<>(all);
        return sysUserInfoPageInfo;
    }

    @Override
    public void addUser(SysUserInfo sysUserInfo) {
        //数据校验
        Assert.isTrue(!sysUserInfo.isEmpty(CheckType.ADD), () -> {
            throw new AddDataErrorException("数据为空", MyErrorCode.DATA_NULL);
        });
        //数据格式校验
        //手机号格式校验
        if (!(MyStringUtils.check(sysUserInfo.getPhone(), MyStringUtils.mobileRegex))){
            throw new AddDataErrorException("手机号不合法",MyErrorCode.PHONE_REG_NOT_MACTH );
        }
        //验证邮箱
        if (!(MyStringUtils.check(sysUserInfo.getEmail(), MyStringUtils.emaiRegex))){
            throw new AddDataErrorException("邮箱不合法",MyErrorCode.MAIL_REG_NOT_MACTH );
        }
        //校验对象中的数据是否已经存在
        //用户名，手机号，邮箱必须检查
        SysUserInfo info = sysUserInfoMapper.findByUserName(sysUserInfo.getUsername());
        if (info!=null){
            throw new AddDataErrorException("用户名已存在",MyErrorCode.USER_ALREADY_EXIST );
        }
        //手机号
        SysUserInfo info1 = sysUserInfoMapper.findByUserPhone(sysUserInfo.getPhone());
        if (info1!=null){
            throw new AddDataErrorException("手机号已存在",MyErrorCode.PHONE_ALREDY_EXIST );
        }
        //邮箱
        SysUserInfo info2 = sysUserInfoMapper.findByUserEmail(sysUserInfo.getEmail());
        if (info2!=null){
            throw new AddDataErrorException("邮箱已存在",MyErrorCode.EMAIL_ALREDY_EXIST );
        }
        //生成盐
        String salt= MD5Utils.getRandomString(10);
        sysUserInfo.setPwdSalt(salt);//给对象赋值刚刚生成的盐
        String md5 = MD5Utils.getMd5(sysUserInfo.getPassword(), salt, 2000);//拿到了密码的md5值
        sysUserInfo.setPassword(md5);
        //设置默认的数据
        sysUserInfo.setCreateBy(sysUserInfo.getUsername());
        sysUserInfo.setRemark("普通用户");
        sysUserInfo.setCreateTime(new Date());
        //保存到数据库
        sysUserInfoMapper.addUser(sysUserInfo);
    }

    @Override
    public SysUserInfo login(UserDto userDto) {
        //用户传递的账号和密码不能是空，如果是空的抛出一个异常
        Assert.isTrue(StringUtils.hasText(userDto.getUsername())&&StringUtils.hasText(userDto.getPassword()),()->{
            throw new QueryDataErrorException("用户名或密码未传递",MyErrorCode.DATA_NULL);
        } );
        //根据用户的名字查询用户数据
        SysUserInfo userInfo = sysUserInfoMapper.findByUserName(userDto.getUsername());
        //拿到用户对象后，获取对象中的盐
        String pwdSalt = userInfo.getPwdSalt();
        //用盐将传递过来的密码转成散列值，必须按照以前的规则
        String md5 = MD5Utils.getMd5(userDto.getPassword(), pwdSalt, 2048);
        //将转成的散列值和从对象中查询到的散列值进行比较，无论成功和失败都要记录日志
        Assert.isTrue(md5.equalsIgnoreCase(userInfo.getPassword()),()->{
            applicationContext.publishEvent(new MyLoginEvent(LoginResultEnum.FAIL,userDto.getUsername()));
            throw new QueryDataErrorException("用户名或密码错误",MyErrorCode.USERNAME_PASSWORD_ERROR );
        } );
        //防止将密码泄漏到前端。需要把根据用户名查询过来的userinfo对象的密码和盐滞空
        userInfo.setPassword(null);
        userInfo.setPwdSalt(null);
        //密码一样就返回userInfo对象
        applicationContext.publishEvent(new MyLoginEvent(LoginResultEnum.SUCCESS,userDto.getUsername()));
        return userInfo;
    }
}
